﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.Exceptions;
using YJKSparseDistance.ModelLayer;
using YJKSparseDisOfRoom.Properties;
using YJKRevitKernel.CurveUtility;
using YJKGeometryFuncs;
using YJKInspectModelFunc;
using YJKInspectModelFunc.ModelLayer;

namespace YJKSparseDisOfRoom.ModelLayer
{
	public class RoomNodePacker
	{
		public RoomPacker_EvacuationDistance ThisRoomPacker
		{
			get
			{
				return this.m_thisRoomPacker;
			}
			private set
			{
				this.m_thisRoomPacker = value;
			}
		}

		public List<EndPointPacker_InRoom> LstStartPoints
		{
			get
			{
				return this.m_lstStartPoints;
			}
			private set
			{
				this.m_lstStartPoints = value;
			}
		}

		public List<EndPointPacker_InRoom> LstEndPoints
		{
			get
			{
				return this.m_lstEndPoints;
			}
			private set
			{
				this.m_lstEndPoints = value;
			}
		}

		public NodeConditionEnum CalculateConditionEnum
		{
			get
			{
				return this.m_calculateConditionEnum;
			}
			set
			{
				this.m_calculateConditionEnum = value;
			}
		}

		public List<ResultBean_InRoom> LstAllResults
		{
			get
			{
				return this.m_lstAllResults;
			}
			set
			{
				this.m_lstAllResults = value;
			}
		}

		internal List<KeyValuePair<EndPointPacker_InRoom, DoorLinkNode>> LstKVPEndPoint
		{
			get
			{
				return this.m_lstKVPEndPoint;
			}
			private set
			{
				this.m_lstKVPEndPoint = value;
			}
		}

		public List<DoorPacker_EvacuationDistance> LstOutSideWallDoors
		{
			get
			{
				return this.m_lstOutSideWallDoors;
			}
			private set
			{
				this.m_lstOutSideWallDoors = value;
			}
		}

		public List<DoorPacker_EvacuationDistance> LstInnerDoors
		{
			get
			{
				return this.m_lstInnerDoors;
			}
			private set
			{
				this.m_lstInnerDoors = value;
			}
		}

		public List<DoorPacker_EvacuationDistance> LstInDoors
		{
			get
			{
				return this.m_lstInDoors;
			}
			private set
			{
				this.m_lstInDoors = value;
			}
		}

		public List<DoorPacker_EvacuationDistance> LstSameLevelDoors
		{
			get
			{
				return this.m_lstSameLevelDoors;
			}
			private set
			{
				this.m_lstSameLevelDoors = value;
			}
		}

		public List<DoorPacker_EvacuationDistance> LstOutDoors
		{
			get
			{
				return this.m_lstOutDoors;
			}
			private set
			{
				this.m_lstOutDoors = value;
			}
		}

		public static void ResetDic()
		{
			RoomNodePacker.m_DicOfPackerAndNode = new Dictionary<RoomPacker_EvacuationDistance, RoomNodePacker>();
			DoorLinkNode.Reset();
		}

		public static RoomNodePacker GetARoomNode(RoomPacker_EvacuationDistance inputPacker)
		{
			if (!RoomNodePacker.IfHasInputRoomPacker(inputPacker))
			{
				RoomNodePacker value = new RoomNodePacker(inputPacker);
				RoomNodePacker.m_DicOfPackerAndNode.Add(inputPacker, value);
			}
			return RoomNodePacker.m_DicOfPackerAndNode[inputPacker];
		}

		public void PrePareAllEndPoints()
		{
			List<EndPointPacker_InRoom> inputlstAppend = new List<EndPointPacker_InRoom>();
			this.PrePareDoors();
			inputlstAppend = this.GetAppenStartPoint();
			this.PrepareOtherStartPoint(inputlstAppend);
			this.PrepareEndPointAppendNode();
		}

		public void MakeAppendBeanInThisRoom()
		{
			List<ResultBean_InRoom> list = null;
			foreach (IGrouping<BaseElementPacker, ResultBean_InRoom> grouping in from k in this.LstAllResults
			group k by k.StartPoint.UseElementPacker)
			{
				if (grouping.Key != null && grouping.Key is DoorPacker_EvacuationDistance)
				{
					list = new List<ResultBean_InRoom>();
					foreach (ResultBean_InRoom item in grouping)
					{
						list.Add(item);
					}
					DoorLinkNode.SetADoorLinkNode(grouping.Key as DoorPacker_EvacuationDistance, list);
				}
			}
		}

		public RoomCurveBean GetCurveBean()
		{
			if (this.m_thisCurveBean == null)
			{
				this.m_thisCurveBean = new RoomCurveBean(this);
			}
			return this.m_thisCurveBean;
		}

		public List<RoomNodePacker> GetNearRoomNodePacker()
		{
			return (from n in this.ThisRoomPacker.LstNearBaseRoomPacker
			where n != null && n is RoomPacker_EvacuationDistance
			select RoomNodePacker.GetARoomNode(n as RoomPacker_EvacuationDistance)).ToList<RoomNodePacker>();
		}

		private bool IfIsSparay()
		{
			return false;
		}

		private List<EndPointPacker_InRoom> GetAppenStartPoint()
		{
			List<EndPointPacker_InRoom> list = new List<EndPointPacker_InRoom>();
			if (this.LstOutDoors.Count <= 1)
			{
				return list;
			}
			List<DoorPacker_EvacuationDistance> list2 = (from k in this.LstOutDoors
			orderby this.ThisRoomPacker.CalculateNearMidPoint(k).DistanceTo(XYZ.Zero)
			select k).ToList<DoorPacker_EvacuationDistance>();
			for (int i = 0; i < list2.Count - 1; i++)
			{
				list.AddRange(this.GetAppendEndPointByInputDoor(list2[i], list2[i + 1]));
			}
			return list;
		}

		private List<EndPointPacker_InRoom> GetAppendEndPointByInputDoor(DoorPacker_EvacuationDistance inputDoorOne, DoorPacker_EvacuationDistance inputDoorTwo)
		{
			bool ifSparay = this.IfIsSparay();
			Room thisRoom = this.ThisRoomPacker.ThisRoom;
			List<EndPointPacker_InRoom> list = new List<EndPointPacker_InRoom>();
			if (this.ThisRoomPacker.ParentAreaPacker == null)
			{
				return list;
			}
			Area inputArea = this.ThisRoomPacker.ParentAreaPacker.ThisElement as Area;
			XYZ xyz = this.ThisRoomPacker.CalculateNearMidPoint(inputDoorOne);
			XYZ xyz2 = this.ThisRoomPacker.CalculateNearMidPoint(inputDoorTwo);
			if (xyz == null || xyz2 == null || xyz.IsAlmostEqualTo(xyz2))
			{
				return list;
			}
			Line line;
			try
			{
				line = Line.CreateBound(xyz, xyz2);
			}
			catch (ArgumentsInconsistentException)
			{
				return list;
			}
			XYZ xyz3 = line.Evaluate(0.5, true);
			XYZ xyz4 = new XYZ(line.Direction.Y, -line.Direction.X, line.Direction.Z);
			Line line2 = Line.CreateUnbound(xyz3, xyz4);
			double parameter = line2.Project(xyz3).Parameter;
			EndPointPacker_InRoom endPointPacker_InRoom = null;
			double val = double.MaxValue;
			EndPointPacker_InRoom endPointPacker_InRoom2 = null;
			double val2 = double.MinValue;
			foreach (List<BaseBoundarySegmentPacker> list2 in this.ThisRoomPacker.LstLstBoundarySegmentPacker)
			{
				foreach (BaseBoundarySegmentPacker baseBoundarySegmentPacker in list2)
				{
					XYZ xyz5;
					if (CurveUtilityMethod.TryGetIntersectionPoint(baseBoundarySegmentPacker.BoundaryCurve, line2, out xyz5))
					{
						double parameter2 = line2.Project(xyz5).Parameter;
						if (!Geometry.Lessthan_Or_Equal(parameter2, parameter) && Geometry.LessThan(parameter2, val))
						{
							val = parameter2;
							endPointPacker_InRoom = new EndPointPacker_InRoom(baseBoundarySegmentPacker, inputArea, thisRoom, xyz5, true, ifSparay);
						}
						else if (Geometry.LessThan(parameter2, parameter) && !Geometry.Lessthan_Or_Equal(parameter2, val2))
						{
							val2 = parameter2;
							endPointPacker_InRoom2 = new EndPointPacker_InRoom(baseBoundarySegmentPacker, inputArea, thisRoom, xyz5, true, ifSparay);
						}
					}
				}
			}
			if (endPointPacker_InRoom != null)
			{
				list.Add(endPointPacker_InRoom);
			}
			if (endPointPacker_InRoom2 != null)
			{
				list.Add(endPointPacker_InRoom2);
			}
			return list;
		}

		private void PrepareEndPointAppendNode()
		{
			this.LstKVPEndPoint = new List<KeyValuePair<EndPointPacker_InRoom, DoorLinkNode>>();
			this.PrepareEndPoint();
			foreach (EndPointPacker_InRoom endPointPacker_InRoom in this.LstEndPoints)
			{
				if (endPointPacker_InRoom.UseElementPacker != null && endPointPacker_InRoom.UseElementPacker is DoorPacker_EvacuationDistance)
				{
					DoorLinkNode adoorLinkNode = DoorLinkNode.GetADoorLinkNode(endPointPacker_InRoom.UseElementPacker as DoorPacker_EvacuationDistance);
					this.LstKVPEndPoint.Add(new KeyValuePair<EndPointPacker_InRoom, DoorLinkNode>(endPointPacker_InRoom, adoorLinkNode));
				}
			}
		}

		private void PrepareEndPoint()
		{
			if (this.ThisRoomPacker.ParentAreaPacker == null)
			{
				return;
			}
			bool ifSparay = this.IfIsSparay();
			Area thisArea = this.ThisRoomPacker.ParentAreaPacker.ThisElement as Area;
			Room thisRoom = this.ThisRoomPacker.ThisRoom;
			List<EndPointPacker_InRoom> list = new List<EndPointPacker_InRoom>();
			list.AddRange(this.AppendDoorEndPoint(this.LstOutDoors, ifSparay, thisArea, thisRoom));
			this.LstEndPoints = new List<EndPointPacker_InRoom>();
			this.LstEndPoints = list;
		}

		private void PrepareOtherStartPoint(List<EndPointPacker_InRoom> inputlstAppend)
		{
			bool ifSparay = this.IfIsSparay();
			Room thisRoom = this.ThisRoomPacker.ThisRoom;
			List<EndPointPacker_InRoom> list = new List<EndPointPacker_InRoom>();
			int num = 0;
			if (this.ThisRoomPacker.ParentAreaPacker == null)
			{
				return;
			}
			Area area = this.ThisRoomPacker.ParentAreaPacker.ThisElement as Area;
			foreach (List<BaseBoundarySegmentPacker> list2 in this.ThisRoomPacker.LstLstBoundarySegmentPacker)
			{
				foreach (BaseBoundarySegmentPacker baseBoundarySegmentPacker in list2)
				{
					if (!(null == baseBoundarySegmentPacker.BoundaryCurve))
					{
						XYZ endPoint = baseBoundarySegmentPacker.BoundaryCurve.GetEndPoint(num);
						list.Add(new EndPointPacker_InRoom(baseBoundarySegmentPacker, area, thisRoom, endPoint, true, ifSparay));
					}
				}
			}
			list.AddRange(this.AppendDoorEndPoint(this.LstInDoors, ifSparay, area, thisRoom));
			this.LstStartPoints = new List<EndPointPacker_InRoom>();
			this.LstStartPoints = list;
			this.LstStartPoints.AddRange(inputlstAppend);
		}

		private List<EndPointPacker_InRoom> AppendDoorEndPoint(List<DoorPacker_EvacuationDistance> inputlstDoorPacker, bool ifSparay, Area thisArea, Room thisRoom)
		{
			List<EndPointPacker_InRoom> list = new List<EndPointPacker_InRoom>();
			foreach (DoorPacker_EvacuationDistance doorPacker_EvacuationDistance in inputlstDoorPacker)
			{
				XYZ xyz = this.ThisRoomPacker.CalculateNearMidPoint(doorPacker_EvacuationDistance);
				if (xyz != null)
				{
					list.Add(new EndPointPacker_InRoom(doorPacker_EvacuationDistance, thisArea, thisRoom, xyz, true, ifSparay));
				}
			}
			return list;
		}

		private void PrePareDoors()
		{
			double num = double.Parse(Resources.str_LimitValueOfDoorWidth);
			num = AssistFunc.mmToFeet(num * 1000.0);
			foreach (BaseDoorPacker baseDoorPacker in this.m_thisRoomPacker.LstBaseDoorPacker)
			{
				if (baseDoorPacker != null && baseDoorPacker is DoorPacker_EvacuationDistance)
				{
                    List<BaseRoomPacker> list = YJKInspectModelFunc.Utility.FindOtherConctRoomsExpectInput(baseDoorPacker, this.ThisRoomPacker);
                    if (baseDoorPacker.LstConctRooms.Count == 1 && YJKInspectModelFunc.Utility.IfDoorIsOutSideDoor(baseDoorPacker))
					{
						double val;
						if (this.TryGetDoorWidth(baseDoorPacker.ThisDoor, out val) && !Geometry.Lessthan_Or_Equal(val, num) && Regex.IsMatch(baseDoorPacker.FamilySymboleName, Resources.str_KeyNameOfActingDoor))
						{
							this.LstOutSideWallDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
						}
					}
					else if (baseDoorPacker.LstConctRooms.Count == 1)
					{
						this.LstInnerDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
					}
					else if (list.Count == 1 && list[0] != null && list[0] is RoomPacker_EvacuationDistance)
					{
						switch (RoomNodePacker.GetARoomNode(list[0] as RoomPacker_EvacuationDistance).CalculateConditionEnum)
						{
						case NodeConditionEnum.NoneCalculate:
							this.LstInDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
							break;
						case NodeConditionEnum.PrepareCalculate:
							this.LstSameLevelDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
							break;
						case NodeConditionEnum.FinishCalculate:
							this.LstOutDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
							break;
						case NodeConditionEnum.IsRunWay:
							this.LstOutDoors.Add(baseDoorPacker as DoorPacker_EvacuationDistance);
							break;
						}
					}
				}
			}
			this.LstOutDoors.AddRange(this.LstOutSideWallDoors);
		}

		private bool TryGetDoorWidth(FamilyInstance inputElement, out double width)
		{
			width = 0.0;
			FamilySymbol symbol = inputElement.Symbol;
			if (symbol == null)
			{
				return false;
			}
			Parameter parameter = symbol.get_Parameter((BuiltInParameter)Enum.Parse(typeof(BuiltInParameter), Resources.str_WidthParam));
			if (parameter == null || (int)parameter.StorageType != 2)
			{
				return false;
			}
			width = parameter.AsDouble();
			return true;
		}

		private RoomNodePacker(RoomPacker_EvacuationDistance inputPacker)
		{
			this.m_thisRoomPacker = inputPacker;
		}

		private static bool IfHasInputRoomPacker(RoomPacker_EvacuationDistance input)
		{
			return RoomNodePacker.m_DicOfPackerAndNode.ContainsKey(input);
		}

		private static Dictionary<RoomPacker_EvacuationDistance, RoomNodePacker> m_DicOfPackerAndNode = new Dictionary<RoomPacker_EvacuationDistance, RoomNodePacker>();

		private RoomPacker_EvacuationDistance m_thisRoomPacker;

		private List<DoorPacker_EvacuationDistance> m_lstOutDoors = new List<DoorPacker_EvacuationDistance>();

		private List<DoorPacker_EvacuationDistance> m_lstOutSideWallDoors = new List<DoorPacker_EvacuationDistance>();

		private List<DoorPacker_EvacuationDistance> m_lstInDoors = new List<DoorPacker_EvacuationDistance>();

		private List<DoorPacker_EvacuationDistance> m_lstInnerDoors = new List<DoorPacker_EvacuationDistance>();

		private List<DoorPacker_EvacuationDistance> m_lstSameLevelDoors = new List<DoorPacker_EvacuationDistance>();

		private List<EndPointPacker_InRoom> m_lstStartPoints = new List<EndPointPacker_InRoom>();

		private List<EndPointPacker_InRoom> m_lstEndPoints = new List<EndPointPacker_InRoom>();

		private List<KeyValuePair<EndPointPacker_InRoom, DoorLinkNode>> m_lstKVPEndPoint = new List<KeyValuePair<EndPointPacker_InRoom, DoorLinkNode>>();

		private List<ResultBean_InRoom> m_lstAllResults = new List<ResultBean_InRoom>();

		private NodeConditionEnum m_calculateConditionEnum;

		private RoomCurveBean m_thisCurveBean;
	}
}
